home *** CD-ROM | disk | FTP | other *** search
/ Java Developer's Companion / Java Developer's Companion.iso / binaries / Windows / jsdk / src / sun / servlet / http / HttpServer.java next >
Encoding:
Java Source  |  1997-07-18  |  11.7 KB  |  456 lines

  1. /*
  2.  * @(#)HttpServer.java    1.20 97/06/16
  3.  * 
  4.  * Copyright (c) 1995-1997 Sun Microsystems, Inc. All Rights Reserved.
  5.  * 
  6.  * This software is the confidential and proprietary information of Sun
  7.  * Microsystems, Inc. ("Confidential Information").  You shall not
  8.  * disclose such Confidential Information and shall use it only in
  9.  * accordance with the terms of the license agreement you entered into
  10.  * with Sun.
  11.  * 
  12.  * SUN MAKES NO REPRESENTATIONS OR WARRANTIES ABOUT THE SUITABILITY OF THE
  13.  * SOFTWARE, EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
  14.  * IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
  15.  * PURPOSE, OR NON-INFRINGEMENT. SUN SHALL NOT BE LIABLE FOR ANY DAMAGES
  16.  * SUFFERED BY LICENSEE AS A RESULT OF USING, MODIFYING OR DISTRIBUTING
  17.  * THIS SOFTWARE OR ITS DERIVATIVES.
  18.  * 
  19.  * CopyrightVersion 1.0
  20.  */
  21.  
  22. package sun.servlet.http;
  23.  
  24. import sun.servlet.*;
  25. import sun.servlet.util.*;
  26. import javax.servlet.*;
  27. import java.net.*;
  28. import java.io.*;
  29. import java.util.Enumeration;
  30. import java.util.Properties;
  31.  
  32. /**
  33.  * This class implements a simple HTTP server for testing servlets.
  34.  *
  35.  * @version    1.20, 06/16/97
  36.  * @author    David Connelly
  37.  */
  38. public
  39. class HttpServer implements Runnable, ServletContext {
  40.     /**
  41.      * The queue of pending connections.
  42.      */
  43.     protected Queue connections;
  44.  
  45.     /**
  46.      * The thread group for connection handlers.
  47.      */
  48.     protected ThreadGroup handlers;
  49.  
  50.     /**
  51.      * The maximum number of handler threads.
  52.      */
  53.     protected int maxHandlers = 100;
  54.  
  55.     /**
  56.      * The handler timeout in milliseconds.
  57.      */
  58.     protected int timeout = 5000;
  59.  
  60.     /**
  61.      * The server port number.
  62.      */
  63.     protected int port = 8080;
  64.  
  65.     /**
  66.      * The backlog parameter to use when creating the server socket.
  67.      */
  68.     protected int backlog = 50;
  69.  
  70.     /**
  71.      * The number of available handlers.
  72.      */
  73.     protected int avail;
  74.  
  75.     /**
  76.      * The total number of active handlers.
  77.      */
  78.     protected int total;
  79.  
  80.     /**
  81.      * The name of the server.
  82.      */
  83.     protected String name = "ServletServer/1.0";
  84.  
  85.     /**
  86.      * The host name of the server.
  87.      */
  88.     protected String host;
  89.  
  90.     /**
  91.      * The directory where servlets are stored.
  92.      */
  93.     protected String servletDir = ".";
  94.  
  95.     /**
  96.      * The properties list that decribes servlet aliases and init args
  97.      */
  98.     protected Properties servletProps = new Properties();
  99.  
  100.     /**
  101.      * The filename which contains servlet properties
  102.      */
  103.     protected String servletPropFile = 
  104.     servletDir+System.getProperty("file.separator")+"servlets.properties";
  105.  
  106.     /**
  107.      * The document root directory for serving files.
  108.      */
  109.     protected String documentDir = ".";
  110.  
  111.     /**
  112.      * The servlet loader.
  113.      */
  114.     protected ServletLoader loader;
  115.  
  116.     /**
  117.      * Set to true for verbose output.
  118.      */
  119.     protected boolean verbose;
  120.  
  121.     /**
  122.      * Creates a new HTTP server with default parameters.
  123.      */
  124.     public HttpServer() {
  125.     }
  126.  
  127.     /**
  128.      * Creates a new HTTP server with parameters from the specified properties.
  129.      * @param props the server properties
  130.      */
  131.     public HttpServer(Properties props) {
  132.     loadProperties(props);
  133.     }
  134.  
  135.     /**
  136.      * Load servlet properties from servlet prop file
  137.      */
  138.     private void loadServletProps() {
  139.     File spf = new File(servletPropFile);
  140.     if (spf.exists() && spf.canRead()) {
  141.         try {
  142.             servletProps.load(new FileInputStream(spf));
  143.             } catch (IOException ioe) {
  144.         System.err.println("Could not load servlet properites file");
  145.         ioe.printStackTrace(System.err);
  146.         }
  147.  
  148.     }
  149.     }
  150.  
  151.     /**
  152.      * Loads parameters from the specified properties.
  153.      * @param props the server properties
  154.      */
  155.     public void loadProperties(Properties props) {
  156.     host = getHostName();
  157.     port = getIntProperty(props, "server.port", port);
  158.     backlog = getIntProperty(props, "server.backlog", backlog);
  159.     maxHandlers = getIntProperty(props, "server.max.handlers", maxHandlers);
  160.     timeout = getIntProperty(props, "server.timeout", timeout);
  161.     name = props.getProperty("server.name", name);
  162.     servletDir = props.getProperty("servlet.dir", servletDir);
  163.     documentDir = props.getProperty("document.dir", documentDir);
  164.     servletPropFile = props.getProperty("servlet.propfile",
  165.         servletPropFile);
  166.     loadServletProps();
  167.     }
  168.  
  169.     /**
  170.      * Returns the integer value of a property.
  171.      */
  172.     protected static int getIntProperty(Properties prop, String name, int def) {
  173.     String s = prop.getProperty(name);
  174.     try {
  175.         return s != null ? Integer.parseInt(s) : def;
  176.     } catch (NumberFormatException e) {
  177.         return def;
  178.     }
  179.     }
  180.  
  181.     /**
  182.      * Returns the host name of the server.
  183.      */
  184.     protected static String getHostName() {
  185.     InetAddress addr;
  186.     try {
  187.         addr = InetAddress.getLocalHost();
  188.     } catch (UnknownHostException e) {
  189.         return null;
  190.     }
  191.     String host = addr.getHostName();
  192.     if (host == null || host.indexOf('.') == -1) {
  193.         // If host does not appear to be fully qualified, then return
  194.         // just the inet address instead.
  195.         host = addr.getHostAddress();
  196.     }
  197.     return host;
  198.     }
  199.  
  200.     /**
  201.      * Runs the servlet server.
  202.      */
  203.     public void run() {
  204.     if (verbose) {
  205.         printSettings();
  206.     }
  207.     // initialize connection handlers
  208.     connections = new Queue(maxHandlers);
  209.     handlers = new ThreadGroup("ServletServer-");
  210.     // create servlet loader
  211.     loader = new ServletLoader(servletDir);
  212.     // create server socket for accepting new connections
  213.     ServerSocket ss;
  214.     try {
  215.         ss = new ServerSocket(port, backlog);
  216.     } catch (IOException e) {
  217.         e.printStackTrace();
  218.         return;
  219.     }
  220.     // accept and dispatch connections
  221.     while (true) {
  222.         // first wait for an available handler thread
  223.         synchronized (this) {
  224.         while (avail <= 0) {
  225.             // fork a new handler if none are currently available
  226.             if (total < maxHandlers) {
  227.             Runnable handler = new HttpServerHandler(this);
  228.             new Thread(handlers, handler).start();
  229.             total++;
  230.             }
  231.             // wait for handler to become ready
  232.             try {
  233.             wait();
  234.             } catch (InterruptedException e) {
  235.             }
  236.         }
  237.         --avail;
  238.         }
  239.         // got handler, now accept new connection and add to queue
  240.         Socket s = null;
  241.         try {
  242.         s = ss.accept();
  243.         putConnection(s);
  244.         } catch (IOException e) {
  245.         e.printStackTrace();
  246.         if (s != null) {
  247.             try {
  248.             s.close();
  249.             } catch (IOException ee) {
  250.             }
  251.         }
  252.         }
  253.     }
  254.     }
  255.  
  256.     /**
  257.      * Places the specified connection object on the queue of pending
  258.      * connections.
  259.      */
  260.     protected void putConnection(Object s) {
  261.     try {
  262.         synchronized (connections) {
  263.         connections.add(s);
  264.         connections.notify();
  265.         }
  266.     } catch (QueueFullException e) {
  267.         throw new InternalError("connection queue overflow");
  268.     }
  269.     }
  270.  
  271.     /**
  272.      * Called by a connection handler to retrieve the next connection from
  273.      * the queue. If this method returns null then the handler should
  274.      * exit as the thread has expired.
  275.      */
  276.     public Object getConnection() {
  277.     // indicate that we are ready for a new connection
  278.     long timeout;
  279.     synchronized (this) {
  280.         timeout = this.timeout;
  281.         avail++;
  282.         notify();
  283.     }
  284.     // wait for next connection
  285.     while (true) {
  286.         long initial = System.currentTimeMillis();
  287.         long elapsed = 0;
  288.         synchronized (connections) {
  289.         while (connections.empty() && elapsed < timeout) {
  290.             try {
  291.             connections.wait(timeout - elapsed);
  292.             } catch (InterruptedException e) {
  293.             }
  294.             elapsed = System.currentTimeMillis() - initial;
  295.         }
  296.         try {
  297.             return connections.remove();
  298.         } catch (QueueEmptyException e) {
  299.             // timed out while waiting for connection
  300.         }
  301.         }
  302.         // timed out so return null to indicate that the handler
  303.         // should exit
  304.         --total;
  305.         --avail;
  306.         return null;
  307.     }
  308.     }
  309.  
  310.     /**
  311.      * Gets a servlet by name.
  312.      */
  313.     public Servlet getServlet(String name) {
  314.     // First attempt to resolve the name against the prop file
  315.         String cname = servletProps.getProperty("servlet."+name+".code");
  316.     String init = servletProps.getProperty("servlet."+name+".initArgs");
  317.     try {
  318.         if (cname == null) {
  319.            return loader.loadServlet(name, 
  320.                      new HttpServletConfig( this, 
  321.                                    init));
  322.             } else {
  323.         return loader.loadServlet(cname, 
  324.                       new HttpServletConfig( this,
  325.                                 init));
  326.         }
  327.     } catch (ServletException e) {
  328.         e.printStackTrace();
  329.         return null;
  330.     }
  331.     }
  332.  
  333.     /**
  334.      * Enumerates the servlets in this server.
  335.      */
  336.     public Enumeration getServlets() {
  337.     return loader.getServlets();
  338.     }
  339.  
  340.     /**
  341.      * Writes a message to the servlet log.
  342.      */
  343.     public void log(String msg) {
  344.     System.err.println(msg);
  345.     }
  346.  
  347.     /**
  348.      * Returns the mime type of the specified file.
  349.      */
  350.     public String getMimeType(String name) {
  351.     return HackURLConnection.guessContentTypeFromName(name);
  352.     }
  353.  
  354.     /**
  355.      * Returns the translated path for the specified virtual path.
  356.      */
  357.     public String getRealPath(String path) {
  358.     path = documentDir + path;
  359.     return path.replace('/', File.separatorChar);
  360.     }
  361.  
  362.     /**
  363.      * Returns the name and version of the current server.
  364.      */
  365.     public String getServerInfo() {
  366.     return name;
  367.     }
  368.  
  369.     /**
  370.      * Returns an attribute of the server for the specified key name.
  371.      * @param name the attribute name
  372.      * @return the value of the attribute or null if not found
  373.      */
  374.     public Object getAttribute(String name) {
  375.     return null;
  376.     }
  377.  
  378.     /**
  379.      * Runs the HTTP server.
  380.      */
  381.     public static void main(String args[]) {
  382.     HttpServer server = new HttpServer();
  383.     Properties props = new Properties();
  384.     try {
  385.         for (int i = 0; i < args.length; i++) {
  386.         String arg = args[i];
  387.         if ("-p".equals(arg)) {
  388.             props.put("server.port", args[++i]);
  389.         } else if ("-b".equals(arg)) {
  390.             props.put("server.backlog", args[++i]);
  391.         } else if ("-m".equals(arg)) {
  392.             props.put("server.max.handlers", args[++i]);
  393.         } else if ("-t".equals(arg)) {
  394.             props.put("server.timeout", args[++i]);
  395.         } else if ("-d".equals(arg)) {
  396.             props.put("servlet.dir", args[++i]);
  397.         } else if ("-r".equals(arg)) {
  398.             props.put("document.dir", args[++i]);
  399.         } else if ("-s".equals(arg)) {
  400.             props.put("servlet.propfile", args[++i]);
  401.         } else if ("-v".equals(arg)) {
  402.             server.verbose = true;
  403.         } else {
  404.             server.help();
  405.         }
  406.         }
  407.         server.loadProperties(props);
  408.     } catch (ArrayIndexOutOfBoundsException e) {
  409.         server.help();
  410.     }
  411.     server.run();
  412.     }
  413.  
  414.     protected void printSettings() {
  415.     PrintStream err = System.err;
  416.     err.println("Server settings:");
  417.     err.println("  port = " + port);
  418.     err.println("  backlog = " + backlog);
  419.     err.println("  max handlers = " + maxHandlers);
  420.     err.println("  timeout = " + timeout);
  421.     err.println("  servlet dir = " + servletDir);
  422.     err.println("  document dir = " + documentDir);
  423.     err.println("  servlet propfile = " + servletPropFile);
  424.     }
  425.  
  426.     protected static void help() {
  427.     PrintStream err = System.err;
  428.     err.println("Usage: srun [options]");
  429.     err.println("Options:");
  430.     err.println("  -p port     the port number to listen on");
  431.     err.println("  -b backlog  the listen backlog");
  432.     err.println("  -m max      maximum number of connection handlers");
  433.     err.println("  -t timeout  connection timeout in milliseconds");
  434.     err.println("  -d dir      servlet directory");
  435.     err.println("  -r root     document root directory");
  436.     err.println("  -s filename servlet property file name");
  437.     err.println("  -v          verbose output");
  438.     System.exit(1);
  439.     }
  440.  
  441. }
  442.  
  443. /*
  444.  * This class is used to extend java.net.URLConnection so that we can
  445.  * access the method guessContentTypeFromName(). For some reason, that
  446.  * method is protected even though guessContentTypeFromStream is public.
  447.  */
  448. abstract class HackURLConnection extends URLConnection {
  449.     protected HackURLConnection(URL url) {
  450.     super(url);
  451.     }
  452.     public static String guessContentTypeFromName(String fname) {
  453.     return URLConnection.guessContentTypeFromName(fname);
  454.     }
  455. }
  456.